package org.aldar.bquest.logic.controller.board;

import android.view.MotionEvent;
import org.aldar.bquest.constants.GameCycleState;
import org.aldar.bquest.constants.IConstants;
import org.aldar.bquest.entity.AbilityTileSprite;
import org.aldar.bquest.entity.TileSprite;
import org.aldar.bquest.logic.TilesGameCycle;
import org.aldar.bquest.logic.controller.board.transform.TileBoardTransform;
import org.aldar.bquest.logic.controller.entity.HeroAnimationController;
import org.aldar.bquest.logic.controller.entity.TileBoard;
import org.aldar.bquest.profile.GameProfile;
import org.aldar.bquest.screens.MainBoardScreen;
import org.aldar.bquest.util.Logger;
import org.aldar.bquest.util.TestUtil;
import org.andengine.entity.IEntity;
import org.andengine.entity.modifier.IEntityModifier;
import org.andengine.entity.modifier.MoveModifier;
import org.andengine.entity.scene.IOnSceneTouchListener;
import org.andengine.entity.scene.Scene;
import org.andengine.input.touch.TouchEvent;
import org.andengine.util.modifier.IModifier;

/**
 * User: Daria
 * Date: 5/31/13 8:01 PM
 */
public class TouchAndMoveController implements IOnSceneTouchListener, IConstants {

    private static final Logger LOG = new Logger(TouchAndMoveController.class);


    private static final int SPEED = 4;//移动速度
    public static int sourceRow = -2;
    public static int sourceCol = -2;
    public static int targetRow;
    public static int targetCol;
    private static int moveValue = 0;//交换移动的临时距离

    public static boolean DONT_TOUCH = false;
    public static boolean ILLEGAL_TOUCH = false;

    public static float[] aXY, bXY;

    public static boolean sourceTileMoveFinished = false;
    public static boolean targetTileMoveFinished = false;
    public static boolean backSwap = false;

    public static boolean lastMoveWasMatch = false;

    private static float touchStartX = 0;
    private static float touchStartY = 0;
    private static float maxDistanceX = 0;
    private static float maxDistanceY = 0;

    private static int lockCount = 0;


    @Override
    public boolean onSceneTouchEvent(final Scene pScene, final TouchEvent pSceneTouchEvent) {
        TestUtil.getInstance().updateText("dont touch", "" + DONT_TOUCH);
        TestUtil.getInstance().updateText("illegal touch", "" + ILLEGAL_TOUCH);

        if (ILLEGAL_TOUCH) {
            return false;
        }
        if (pSceneTouchEvent.getAction() == MotionEvent.ACTION_DOWN) {
            if (DONT_TOUCH) {
                if (!ILLEGAL_TOUCH) {
                    lockTouchActions();
                }
                //replaced with function lockTouchActions to count locks
                // ILLEGAL_TOUCH = true;
                return false; //return if screen touched and not yet released!!!
            }
            touchStartX = pSceneTouchEvent.getX();
            touchStartY = pSceneTouchEvent.getY();
            maxDistanceX = 0;
            maxDistanceY = 0;
        }

        float distanceX = Math.abs(touchStartX - pSceneTouchEvent.getX());
        maxDistanceX = Math.max(maxDistanceX, distanceX);

        float distanceY = Math.abs(touchStartY - pSceneTouchEvent.getY());
        maxDistanceY = Math.max(maxDistanceY, distanceY);

        TilesGameCycle.getInstance().setState(GameCycleState.WAITING);
        //reset prepared Hints...
        AvailableMovesCounter.resetHints();

        if (pSceneTouchEvent.getX() > 0 && pSceneTouchEvent.getX() < CAMERA_WIDTH
                && pSceneTouchEvent.getY() > 0 && pSceneTouchEvent.getY() < CAMERA_HEIGHT) {

            int touchedCol = TileBoardTransform.getColFromScreenXY((int) pSceneTouchEvent.getX(), (int) pSceneTouchEvent.getY());
            int touchedRow = TileBoardTransform.getRowFromScreenXY((int) pSceneTouchEvent.getX(), (int) pSceneTouchEvent.getY());
            if (TileBoard.isCellValid(touchedCol, touchedRow)) {
                if (pSceneTouchEvent.getAction() == MotionEvent.ACTION_DOWN) {
                    if (DONT_TOUCH) {
                        if (!ILLEGAL_TOUCH) {
                            lockTouchActions();
                        }
                        return false; //return if screen touched and not yet released!!!
                    }
                    DONT_TOUCH = true;
                    sourceRow = TileBoardTransform.getRowFromScreenXY((int) pSceneTouchEvent.getX(), (int) pSceneTouchEvent.getY());
                    sourceCol = TileBoardTransform.getColFromScreenXY((int) pSceneTouchEvent.getX(), (int) pSceneTouchEvent.getY());
                    LOG.i("Drag Source col/row: " + sourceCol + " " + sourceRow);
                    aXY = new float[]{pSceneTouchEvent.getX(), pSceneTouchEvent.getY()};

                    //test bug wrong direction!!!
                    //FIXED 28.01.15
                    /*
                    MainBoardScreen.getScm().getTestLayer().detachChildren();
                    Rectangle r = new Rectangle(pSceneTouchEvent.getX(), pSceneTouchEvent.getY(), 5, 5, MainBoardScreen.getScm().getVertexBufferObjectManager());
                    r.setColor(Color.RED);
                    MainBoardScreen.getScm().getTestLayer().attachChild(r);
                    Text myText = new Text(pSceneTouchEvent.getX(), pSceneTouchEvent.getY(), ResourceManager.getInstance().getBigFont(), sourceCol + "_" + sourceRow, 100, MainBoardScreen.getScm().getVertexBufferObjectManager());
                    myText.setScale(0.5f);
                    MainBoardScreen.getScm().getTestLayer().attachChild(myText);
                    */

                } else if (pSceneTouchEvent.getAction() == MotionEvent.ACTION_MOVE) {
                    int adjacence = TileBoard.getAdjacence(
                            TileBoardTransform.getColFromScreenXY((int) pSceneTouchEvent.getX(), (int) pSceneTouchEvent.getY()),
                            TileBoardTransform.getRowFromScreenXY((int) pSceneTouchEvent.getX(), (int) pSceneTouchEvent.getY()));
                    if (adjacence != NON_ADJACENT && TileBoard.getSourceTile().isDragDropAllowed()) {
                        float[] dragDropCoords = TileBoardTransform.getDragDropCoords(
                                pSceneTouchEvent.getX(),
                                pSceneTouchEvent.getY());
                        TileBoard.getSourceTile().setIsInDrag(true);
                        TileBoard.getSourceTile()
                                .setScreenPosition(dragDropCoords[0], dragDropCoords[1]
                                );
                        if (targetRow > 0 && targetCol > 0 && targetCol + targetRow < sourceCol + sourceRow
                                && targetCol < CELLS_HORIZONTAL && targetRow < CELLS_VERTICAL) {
                            //possible fix for the missing child problem in Entity.onManagedUpdate method (when firing bomb eg.)
                            MainBoardScreen.getInstanceEngine().runOnUpdateThread(new Runnable() {
                                @Override
                                public void run() {
                                    TileBoard.getSourceTile().attachToLayer(MainBoardScreen.getScm().getJwLayer()[targetCol][targetRow]);
                                }
                            });
                        }
                    }
                } else {
                    LOG.i("Unhandled touch action = " + pSceneTouchEvent.getAction());
                }
            }
        }

        if (pSceneTouchEvent.getAction() == MotionEvent.ACTION_UP || pSceneTouchEvent.getAction() == MotionEvent.ACTION_CANCEL) {
            if (!ILLEGAL_TOUCH) {
                lockTouchActions();
            }
            if (TileBoard.getSourceTile() != null) {
                if (TileBoard.getSourceTile() instanceof AbilityTileSprite
                        && maxDistanceX < 15 && maxDistanceY < 15) {
                    LOG.i("Ability tile TOUCH - calling perform match action");
                    /*float[] screenXY = TileBoardTransform.getSpriteAdjustedScreenCoords(
                            TileBoardTransform.getScreenCoords(sourceCol * CELL_WIDTH, sourceRow * CELL_HEIGHT));
                    TileBoard.getSourceTile().setScreenPosition(screenXY[0], screenXY[1]);  */
                    Runnable abilityRunnable = new Runnable() {
                        @Override
                        public void run() {
                            ((AbilityTileSprite) TileBoard.getSourceTile()).performAbility(sourceCol, sourceRow);
                        }
                    };
                    if (!HeroAnimationController.getInstance().isWalking() || !((AbilityTileSprite) TileBoard.getSourceTile()).getAbilityType().requiresHeroActions()) {
                        abilityRunnable.run();
                    } else {
                        //hero is walking - requesting ability to trigger
                        HeroAnimationController.getInstance().setAbilityActionRequested(abilityRunnable);
                    }
                    GameProfile.getCurrentLevelProfile().addTurn();
                    return false;
                }
                TouchAndMoveController.setMoveDirection();
                if (TilesGameCycle.getInstance().getState() != GameCycleState.SWAP_TILES) {
                    TileBoard.getSourceTile().setMapPosition(sourceCol, sourceRow);
                }
            } else {
                LOG.i("Something strange happened to touch event... setting back WAITING_SWAP...");
                TilesGameCycle.getInstance().resetToWaitingSwap();
            }
        }
        return false;
    }

    private static void resetTargetCol() {
        targetRow = -2;
        targetCol = -2;
    }


    public static void setMoveDirection() {
        if (TileBoard.isCellValid(sourceCol, sourceRow) && TileBoard.isCellValid(targetCol, targetRow)) {//check if both cells are on the board and not outside
            LOG.i("bquest", "LAST MOVE set for source col row target col row " + sourceCol + sourceRow + "  " + targetCol + targetRow + " sourcetile " + TileBoard.getSourceTile());
            if (sourceRow == targetRow && sourceCol > targetCol) {
                TilesGameCycle.getInstance().setState(GameCycleState.SWAP_TILES);
                TilesGameCycle.setLastMove(GameCycleState.MOVE_LEFT);
                MainBoardScreen.getScm().reorderJwLayersSlash();
            } else if (sourceRow == targetRow && sourceCol < targetCol) {
                TilesGameCycle.getInstance().setState(GameCycleState.SWAP_TILES);
                TilesGameCycle.setLastMove(GameCycleState.MOVE_RIGHT);
                MainBoardScreen.getScm().reorderJwLayersSlash();
            } else if (sourceRow > targetRow && sourceCol == targetCol) {
                TilesGameCycle.getInstance().setState(GameCycleState.SWAP_TILES);
                TilesGameCycle.setLastMove(GameCycleState.MOVE_UP);
                MainBoardScreen.getScm().reorderJwLayersBack();
            } else if (sourceRow < targetRow && sourceCol == targetCol) {
                TilesGameCycle.getInstance().setState(GameCycleState.SWAP_TILES);
                TilesGameCycle.setLastMove(GameCycleState.MOVE_DOWN);
                MainBoardScreen.getScm().reorderJwLayersBack();
            } else {
                TilesGameCycle.getInstance().resetToWaitingSwap();
            }

        } else {
            TilesGameCycle.getInstance().resetToWaitingSwap();
        }
    }

    public static void swapTiles() {
        if (GameCycleState.MOVE_UP == TilesGameCycle.getLastMove())
            doSwappingTiles(0, 1, 0, -1);
        if (GameCycleState.MOVE_DOWN == TilesGameCycle.getLastMove())
            doSwappingTiles(0, -1, 0, 1);
        if (GameCycleState.MOVE_LEFT == TilesGameCycle.getLastMove())
            doSwappingTiles(1, 0, -1, 0);
        if (GameCycleState.MOVE_RIGHT == TilesGameCycle.getLastMove())
            doSwappingTiles(-1, 0, 1, 0);
    }

    private static void moveCells(int x1, int y1, int x2, int y2) {
        final TileSprite sourceTile = TileBoard.getTileMap().get(TileBoard.getKey(sourceRow, sourceCol));
        final TileSprite targetTile = TileBoard.getTileMap().get(TileBoard.getKey(targetRow, targetCol));
        final float[] newCurJewScXY =
                TileBoardTransform.getSpriteAdjustedScreenCoords(TileBoardTransform.getScreenCoords(targetCol * CELL_WIDTH + x1 * CELL_HEIGHT, targetRow * CELL_HEIGHT + y1 * CELL_HEIGHT));
        final float[] newLastJewScXY =
                TileBoardTransform.getSpriteAdjustedScreenCoords(TileBoardTransform.getScreenCoords(sourceCol * CELL_WIDTH + x2 * CELL_HEIGHT, sourceRow * CELL_HEIGHT + y2 * CELL_HEIGHT));
        sourceTile.getTileGroundContainer().registerEntityModifier(new MoveModifier(
                0.2f, sourceTile.getTile().getX(), sourceTile.getTile().getY(), newLastJewScXY[0], newLastJewScXY[1], new IEntityModifier.IEntityModifierListener() {
            @Override
            public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
            }

            @Override
            public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                sourceTile.setIsInDrag(false);
                sourceTileMoveFinished = true;//To change body of implemented methods use File | Settings | File Templates.
                if (sourceTileMoveFinished && targetTileMoveFinished) {
                    if (backSwap) {
                        TilesGameCycle.getInstance().setState(GameCycleState.BACK_SWAP_FINISHED);
                    } else {
                        if (TilesGameCycle.getInstance().getState() == GameCycleState.WAITING) {
                            TilesGameCycle.getInstance().setState(GameCycleState.SWAP_FINISHED);
                        }
                    }
                }
                sourceTile.restartAnimation();
            }
        }));
        sourceTile.getTileAboveContainer().registerEntityModifier(new MoveModifier(
                0.2f, sourceTile.getTile().getX(), sourceTile.getTile().getY(), newLastJewScXY[0], newLastJewScXY[1], new IEntityModifier.IEntityModifierListener() {
            @Override
            public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
            }

            @Override
            public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
            }
        }));
        targetTile.getTileGroundContainer().registerEntityModifier(new MoveModifier(
                0.2f, targetTile.getTile().getX(), targetTile.getTile().getY(), newCurJewScXY[0], newCurJewScXY[1], new IEntityModifier.IEntityModifierListener() {
            @Override
            public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                //To change body of implemented methods use File | Settings | File Templates.
            }

            @Override
            public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                targetTileMoveFinished = true;
                if (sourceTileMoveFinished && targetTileMoveFinished) {
                    if (backSwap) {
                        TilesGameCycle.getInstance().setState(GameCycleState.BACK_SWAP_FINISHED);
                    } else {
                        if (TilesGameCycle.getInstance().getState() == GameCycleState.WAITING) {
                            TilesGameCycle.getInstance().setState(GameCycleState.SWAP_FINISHED);
                        }
                    }
                }
                targetTile.restartAnimation();
            }
        }));
        targetTile.getTileAboveContainer().registerEntityModifier(new MoveModifier(
                0.2f, targetTile.getTile().getX(), targetTile.getTile().getY(), newCurJewScXY[0], newCurJewScXY[1], new IEntityModifier.IEntityModifierListener() {
            @Override
            public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                //To change body of implemented methods use File | Settings | File Templates.
            }

            @Override
            public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
            }
        }));
    }

    private static void resetStateAfterSwap() {
        TilesGameCycle.getInstance().setState(GameCycleState.CHECK);
        sourceRow = -2;
        sourceCol = -2;
        resetTargetCol();
        moveValue = 0;
        sourceTileMoveFinished = false;
        targetTileMoveFinished = false;
    }

    private static void doSwappingTiles(int x1, int y1, int x2, int y2) {
        moveCells(x1, y1, x2, y2);
    }

    private static boolean isSwapMatch() {
        if (MatchedTilesFinder.isTileInMatch(sourceCol, sourceRow) || MatchedTilesFinder.isTileInMatch(targetCol, targetRow)) {
            return true;
        }
        return false;
    }


    public static void onSwapFinished() {
        lastMoveWasMatch = false;
        TileBoard.swapInHashMap(sourceCol, sourceRow, targetCol, targetRow);
        if (isSwapMatch()) {
            lastMoveWasMatch = true;
            resetStateAfterSwap();
            //TileBoard.mFallSound.play();
        } else {
            //TileBoard.mSwapErrorSound.play();
            sourceTileMoveFinished = false;
            targetTileMoveFinished = false;
            backSwap = true;
            swapTiles();
        }
    }

    public static void onBackSwapFinished() {
        TileBoard.swapInHashMap(sourceCol, sourceRow, targetCol, targetRow);
        backSwap = false;
        resetStateAfterSwap();
    }

    public static void lockTouchActions() {
        int oldLockCount = lockCount;
        ILLEGAL_TOUCH = true;
        lockCount++;
        LOG.i(".   lockTouchActions: set ILLEGAL_TOUCH=true. lockCount " + oldLockCount + "->" + lockCount, 1);
    }

    /*public static void unlockTouchActions() {
        if (lockCount > 0) {
            lockCount--;
        }
        LOG.i("unlockTouchActions called. New lockCount = " + lockCount);
        LOG.caller();
        if (lockCount == 0) {
            LOG.i("setting ILLEGAL_TOUCH = false");
            ILLEGAL_TOUCH = false;
        }
    } */

    public static void unlockTouchActions() {
        int oldLockCount = lockCount;
        if (lockCount > 0) {
            lockCount--;
        }
        LOG.i(".unlockTouchActions: set ILLEGAL_TOUCH=false. lockCount " + oldLockCount + "->" + lockCount, 1);
        if (lockCount == 0) {
            ILLEGAL_TOUCH = false;
        }
    }

    public static void resetLocks() {
        lockCount = 0;
        ILLEGAL_TOUCH = false;
        LOG.i(".resetLocks: set ILLEGAL_TOUCH=false. lockCount " + lockCount, 1);
    }

}
